/* NOTICE
 
    Jim - the Java Image Manager, copyright (c) 2005 - 2007 Grant Gardner 
 
    Jim is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    Jim is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
  
    In addition, as a special exception, you are permitted to link this code
    with the 3rd party libraries required to compile this file and to
    distribute a linked combination including them. You must obey the
    GNU General Public License in all respects for all of the code except for
    those 3rd party libraries that are licenced under terms incompatible with the
    GNU General Public License.  
 
 
 NOTICE */

/*
 * Created on 19/06/2004
 *
 */

package au.com.lastweekend.jim.io.exif;

import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import javax.imageio.ImageIO;

import au.com.lastweekend.jim.io.metadata.MetadataException;
import au.com.lastweekend.jim.io.metadata.Tag;

/**
 * 
 * @author grant@lastweekend.com.au
 * @version $Id: ThumbnailTiffDirectoryWriter.java,v 1.4 2006/02/20 12:21:10 ggardner Exp $
 * 
 */
public class ThumbnailTiffDirectoryWriter extends ExifDirectoryWriter {

    private List<Tag> _writableTags;

    public ThumbnailTiffDirectoryWriter(ThumbnailTiffDirectory thumbnailDirectory, ExifTransform transform) {

        super(thumbnailDirectory, transform);
    }

    @Override
    protected int getWriteableTagCount() {

        return getWritableTags().size();
    }

    private List<Tag> getWritableTags() {

        if (_writableTags == null) {
            _writableTags = new ArrayList<Tag>();
            ThumbnailTiffDirectory thumbnailDirectory = getThumbnailTiffDirectory();
            Iterator<Tag> iterator = thumbnailDirectory.getTagIterator();
            while (iterator.hasNext()) {
                Tag tag = iterator.next();
                if (tag.getTagType() != ThumbnailTiffDirectory.TAG_THUMBNAIL_DATA) {
                    _writableTags.add(tag);
                }
            }
        }
        return _writableTags;
    }

    private ThumbnailTiffDirectory getThumbnailTiffDirectory() {

        ThumbnailTiffDirectory thumbnailDirectory = (ThumbnailTiffDirectory) getDirectory();
        return thumbnailDirectory;
    }

    @Override
    protected Iterator<Tag> getWriteableTagIterator() {

        return getWritableTags().iterator();
    }

    @Override
    protected int getTagDataLength(int tagType, Object tagValue, int formatCode) {

        if (isOffsetTag(tagType)) {
            return getThumbnailDataLength();
        } else {
            return super.getTagDataLength(tagType, tagValue, formatCode);
        }
    }

    private boolean isOffsetTag(int tagType) {

        return tagType == ExifDirectory.TAG_THUMBNAIL_OFFSET || tagType == ExifDirectory.TAG_STRIP_OFFSETS;
    }

    private int getThumbnailDataLength() {

        try {
            return getTransformedThumbnailData().length;
        } catch (MetadataException e) {
            throw new MetadataRuntimeException("Unable to get thumbnail while calculating size", e);
        }
    }

    @Override
    protected void writeLongTag(Object longTag, OutputStream output) throws IOException {

        if (longTag instanceof Tag && (isOffsetTag(((Tag) longTag).getTagType()))) {
            try {
                byte[] tagData = getTransformedThumbnailData();
                output.write(tagData);
            } catch (MetadataException e) {
                throw new IOException("Unable to get thumbnail data to write");
            }

        } else {
            super.writeLongTag(longTag, output);
        }
    }

    private byte[] getTransformedThumbnailData() throws MetadataException {

        // TODO Tiff thumbnails will not be rotated.
        if (getExifTransform().isThumbnailTransformRequired() && getThumbnailTiffDirectory().isJPEGCompression()) {
            BufferedImage originalImage = getThumbnailTiffDirectory().getThumbnailImage();
            if (originalImage != null) {
                BufferedImage transformedImage = getExifTransform().transformThumbnail(originalImage);
                ByteArrayOutputStream outStream = new ByteArrayOutputStream();
                try {
                    ImageIO.write(transformedImage, "JPEG", outStream);
                } catch (IOException e) {
                    throw new MetadataException("Unable to rotate thumbnail", e);
                }
                return outStream.toByteArray();
            }
        }

        byte[] thumbnailData = getThumbnailTiffDirectory().getThumbnailData();
        // One of the bogus samples contains a zero length thumbnail...
        if (thumbnailData == null) {
            return new byte[0];
        }
        return thumbnailData;

    }

    @Override
    protected Object getTransformedTagValue(int tagType) {

        if (tagType == ExifDirectory.TAG_THUMBNAIL_LENGTH) {
            return getThumbnailDataLength();
        }

        return super.getTransformedTagValue(tagType);
    }

    @Override
    protected int writeTagInfo(Tag tag, final int nextTagValueOffset, List<Object> longTagList, OutputStream output)
            throws IOException {

        int tagType = tag.getTagType();

        if (isOffsetTag(tagType)) {
            // This is the pointer to the thumbnail data, but it is written as a
            // LONG value, so we need to
            // add it to the long tag list.
            int thumbnailLength = getThumbnailDataLength();

            int newOffset = nextTagValueOffset + thumbnailLength;
            longTagList.add(tag);
            writeOffsetTagInfoHeader(tagType, tag.getFormatCode(), 1, nextTagValueOffset, output);
            return newOffset;
        } else {
            return super.writeTagInfo(tag, nextTagValueOffset, longTagList, output);
        }
    }

    /*
     * @see au.com.lastweekend.jim.io.exif.DirectoryWriter#getComponentCount()
     */
    @Override
    public int getComponentCount() {

        return 1;
    }
}